﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.Serialization;
using GsOrganization.Common.Extend;
using GsOrganization.TcpPacket;
using GsOrganization.Common.Helper;
using GsOrganization.TcpPacket.PayLoad;

namespace GsOrganization.TcpServer
{
    internal class CollectServer
    {
        #region 参数信息
        private SortedList<string, ClientSession> SL_ClientSession = new SortedList<string, ClientSession>();
        private ReaderWriterLock RWL_SL_ClientSession = new ReaderWriterLock();

        private SortedList<string, ClientSession> SL_NoNamedClient = new SortedList<string, ClientSession>();
        private ReaderWriterLock RWL_SL_NoNamedClient = new ReaderWriterLock();

        private Socket ServerSocket;
        private int ServerPort;
        /// <summary>
        /// 客户端超时时间s
        /// </summary>
        private int NamedClientTimeOut = 1800;

        /// <summary>
        /// 未命名客户端超时时间s
        /// </summary>
        private int NonameClientTimeOut = 600;

        private Serilog.ILogger Logger;

        public CollectServer(int ServerPort, Serilog.ILogger Logger)
        {
            this.ServerPort = ServerPort;
            this.Logger = Logger;
        }

        private Thread th_MoniteNonameClient;
        private Thread th_MoniteNamedClient;
        #endregion

        #region 超时监控
        /// <summary>
        /// 使用线程监视已成功连接未成功登录的客户端
        /// </summary>
        private void MoniteNonameClient()
        {
            while (true)
            {
                Thread.Sleep(1000);
                try
                {
                    List<ClientSession> ls_to_dispose = new List<ClientSession>();
                    RWL_SL_NoNamedClient.AcquireWriterLock(-1);
                    for (int i = 0; i < SL_NoNamedClient.Count;)
                    {
                        string key = SL_NoNamedClient.Keys[i].ToString();
                        ClientSession cs = SL_NoNamedClient[key];
                        if (cs.GetLastReceiveClientMessageTimespan() > NonameClientTimeOut)
                        {
                            cs.Dispose();
                            SL_NoNamedClient.Remove(key);
                            ls_to_dispose.Add(cs);
                        }
                        else
                        {
                            i++;
                        }
                    }
                    RWL_SL_NoNamedClient.ReleaseWriterLock();
                    foreach (var cs in ls_to_dispose)
                    {
                        Logger.Warning($"MoniteNonameClient 通信超时已处理：DeviceId=\"{cs.SessionId}\"");
                    }
                }
                catch (Exception ex)
                {

                    Logger.Error(ex, "MoniteNonameClient使用线程监视已成功登录的客户端 error");
                }
            }
        }

        /// <summary>
        /// 使用线程监视已成功登录的客户端
        /// </summary>
        private void MoniteNamedClient()
        {
            while (true)
            {
                Thread.Sleep(3000);
                try
                {
                    List<ClientSession> ls_to_dispose = new List<ClientSession>();
                    RWL_SL_ClientSession.AcquireWriterLock(-1);
                    for (int i = 0; i < SL_ClientSession.Count;)
                    {
                        string key = SL_ClientSession.Keys[i].ToString();
                        ClientSession cs = SL_ClientSession[key] as ClientSession;
                        if (cs.GetLastReceiveClientMessageTimespan() > NamedClientTimeOut)
                        {
                            cs.Dispose();
                            SL_ClientSession.Remove(key);
                            ls_to_dispose.Add(cs);
                        }
                        else
                        {
                            i++;
                        }
                    }
                    RWL_SL_ClientSession.ReleaseWriterLock();
                    foreach (var cs in ls_to_dispose)
                    {
                        Logger.Warning($"MoniteNamedClient  通信超时已处理：DeviceId=\"{cs.ClientId}\",SessionId=\"{cs.SessionId}\"");
                    }
                }
                catch (Exception ex)
                {

                    Logger.Error(ex, "使用线程监视已成功登录的客户端 error");
                }
            }
        }
        #endregion

        #region 内部客户端管理
        private void AddNoNameClient(ClientSession Session)
        {
            RWL_SL_NoNamedClient.AcquireWriterLock(-1);
            SL_NoNamedClient.Add(Session.SessionId, Session);
            RWL_SL_NoNamedClient.ReleaseWriterLock();
        }

        private void RemoveNoNameClient(string SessionId)
        {
            RWL_SL_NoNamedClient.AcquireWriterLock(-1);
            if (SL_NoNamedClient.ContainsKey(SessionId))
            {
                SL_NoNamedClient.Remove(SessionId);
            }
            RWL_SL_NoNamedClient.ReleaseWriterLock();
        }
        public ClientSession GetClientSession(string ClientId)
        {
            ClientSession clientSession = null;
            if (!string.IsNullOrWhiteSpace(ClientId))
            {
                //改造从redis中获取，如果不加锁，并发如何处理
                RWL_SL_ClientSession.AcquireWriterLock(-1);

                if (SL_ClientSession.ContainsKey(ClientId))
                {
                    clientSession = SL_ClientSession[ClientId];
                }
                RWL_SL_ClientSession.ReleaseWriterLock();
            }

            return clientSession;
        }

        /// <summary>
        /// 当连接断开时触发
        /// </summary>
        /// <param name="ClientId"></param>
        private void session_OnClientDisposedEvent(string ClientId, string DBId)
        {
            OnClientDisconnect(ClientId, DBId);
        }

        private int AddNamedClient(ClientSession clientSession)
        {
            int Result = 0;
            string Log = "";
            RWL_SL_ClientSession.AcquireWriterLock(-1);
            if (SL_ClientSession.ContainsKey(clientSession.ClientId))
            {
                Result = 0;
                ClientSession OldSession = SL_ClientSession[clientSession.ClientId];
                Log = "AddNamedClient 连接重复，已覆盖，ClientId=" + clientSession.ClientId + ",OldSIClientId=" + OldSession.SessionId + ",NewSIClientId=" + clientSession.SessionId + ",OldSessiontClientRemoteEndPoint=" + OldSession.GetClientRemoteEndPoint() + ",NewSessiontClientRemoteEndPoint=" + clientSession.GetClientRemoteEndPoint();
                OldSession.Dispose(1);
                SL_ClientSession[clientSession.ClientId] = clientSession;

            }
            else
            {
                SL_ClientSession.Add(clientSession.ClientId, clientSession);
                Log = "AddNamedClientadd，ClientId=" + clientSession.ClientId + ",,NewSIClientId=" + clientSession.SessionId + ",NewSessiontClientRemoteEndPoint=" + clientSession.GetClientRemoteEndPoint();
            }
            RWL_SL_ClientSession.ReleaseWriterLock();
            if (Log != "")
            {
                Logger.Warning(Log);
            }
            return Result;
        }

        private void RemoveNamedClient(string ClientId, string SessionId)
        {
            string Log = "RemoveNamedClient ";
            RWL_SL_ClientSession.AcquireWriterLock(-1);
            if (SL_ClientSession.ContainsKey(ClientId))
            {
                ClientSession cs = SL_ClientSession[ClientId];
                if (cs.SessionId == SessionId)
                {
                    SL_ClientSession.Remove(ClientId);
                    Log += " SIClientId相同ClientId=" + ClientId;
                }
                else
                {
                    Log += " SIClientId不相同，队列中SIClientId=" + cs.SessionId + ",待删除SIClientId=" + SessionId + ",ClientId=" + ClientId;
                }
            }
            RWL_SL_ClientSession.ReleaseWriterLock();
            Logger.Information(Log);
        }

        #endregion

        #region [回调信息设置]

        private SortedList<ushort, Func<ClientSession, Header, byte[], int>> sl_ActionInfos = new SortedList<ushort, Func<ClientSession, Header, byte[], int>>();
        private ReaderWriterLock RWL_Action = new ReaderWriterLock();
        public Func<ClientSession, Header, DeviceLoginReq, DeviceLoginResp> OnRecvLogin { get; set; }
        public Action<string, string> OnClientDisconnect { get; set; }

        private Func<ClientSession, Header, byte[], int> GetAction(ushort Order)
        {
            Func<ClientSession, Header, byte[], int> result = null;
            RWL_Action.AcquireWriterLock(-1);
            if (sl_ActionInfos.ContainsKey(Order))
            {
                result = sl_ActionInfos[Order];
            }
            RWL_Action.ReleaseWriterLock();
            return result;
        }

        internal void SetAction(ushort Order, Func<ClientSession, Header, byte[], int> action)
        {
            if (sl_ActionInfos.ContainsKey(Order))
            {
                sl_ActionInfos[Order] = action;
            }
            else
            {
                sl_ActionInfos.Add(Order, action);
            }
        }
        #endregion

        public int StartServerSocket()
        {

            ServerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            IPEndPoint IPEP = new IPEndPoint(IPAddress.Any, ServerPort);
            int result = 1;
            try
            {
                ServerSocket.Bind(IPEP);
                ServerSocket.Listen(300);//开始监听端口号,10表示一个队列,这个队列最多让十个要进行连接的客户端排队,如果设置为0则可以让无限个要连接的客户端排队让服务端进行处理

                ServerSocket.BeginAccept(new AsyncCallback(AcceptClientCallBack), null/*this.ServerSocket*/);
                //第二个参数就是为了给我们的方法传递参数的.这只是开始接收一个客户端,并没有接受一个客户端.
                //我们把服务器socket传递过去方便在方法中实现与客户端的连接.因为是自身发送的，就可以传null?
                result = 0;
            }
            catch (Exception ex)
            {
                Logger.Error(ex, "StartServerSocket error");
            }
            if (result == 0)
            {
                th_MoniteNamedClient = new Thread(MoniteNamedClient);
                th_MoniteNamedClient.IsBackground = true;
                th_MoniteNonameClient = new Thread(MoniteNonameClient);
                th_MoniteNonameClient.IsBackground = true;
                th_MoniteNamedClient.Start();
                th_MoniteNonameClient.Start();
            }
            return result;
        }
        private void AcceptClientCallBack(IAsyncResult ar)
        {

            try
            {
                Logger.Information("AcceptClientCallBack");
                Socket clientSocket = ServerSocket.EndAccept(ar);
                ClientSession session = new ClientSession(clientSocket, session_OnClientDisposedEvent);
                Logger.Information($"AcceptClientCallBack new ClientSession0:{session.SessionId},{(clientSocket.RemoteEndPoint == null ? "" : ((IPEndPoint)clientSocket.RemoteEndPoint).ToString())}");
                AddNoNameClient(session);
                if (!session.BeginRecvData(new AsyncCallback(ReceiveNoNameClientMessageCallBack)))
                {
                    session.Dispose();
                    RemoveNoNameClient(session.SessionId);
                }
                ServerSocket.BeginAccept(new AsyncCallback(AcceptClientCallBack), null);
            }
            catch (Exception ex)
            {
                Logger.Error(ex, "AcceptClientCallBack2");
                ServerSocket.BeginAccept(new AsyncCallback(AcceptClientCallBack), null);
                return;
            }
        }
        private void ReceiveNoNameClientMessageCallBack(IAsyncResult ar)
        {
            ClientSession clientSession = ar.AsyncState as ClientSession;
            if (clientSession == null)
            {
                Logger.Information("ReceiveNoNameClientMessageCallBack22222");
                return;
            }
            int RecvSize = 0;
            try
            {
                RecvSize = clientSession.EndRecvData(ar);
                if (RecvSize == 0)
                {////客户端正常关闭服务器就会接受到空数据,但是不会报错,所以这里处理正常关闭的情况
                    Logger.Warning($"ReceiveNoNameClientMessageCallBack : RecvSize is zero!... it will be dispose! ClientId:{clientSession.SessionId}");
                    clientSession.Dispose();
                    return;
                }
            }
            catch (Exception ex)
            {
                Logger.Error("ReceiveNoNameClientMessageCallBack Error1:" + ex.Message);
                return;
            }
            DeviceLoginResp dlr = null;
            try
            {
                Logger.Information($"ReceiveNoNameClientMessageCallBack---ClientId:{clientSession.SessionId},RecvSize:{RecvSize}");
                clientSession.SetReceiveClientMessageTime();
                byte[] Total = new byte[RecvSize + clientSession.LastBytes.Length];
                clientSession.LastBytes.CopyTo(Total, 0);
                Array.Copy(clientSession.RecvBuff, 0, Total, clientSession.LastBytes.Length, RecvSize);
                if (Total.Length < IHeader.HeaderLength)
                {
                    Logger.Warning($"ReceiveNoNameClientMessageCallBack : FirstRecvTotalSize Less then HeaderLength... it will be dispose! ClientId:{clientSession.SessionId}");
                    return;
                }
                Logger.Information($"1{Total.Length}||{IHeader.HeaderLength}");
                Header head = new Header(Total, 0);
                if (head.CommandId == MessageCommandId.ServerSendCottonModelCodeRsp)
                {
                    var adf = 9;
                }
                Logger.Information("2" + head.CommandId.ToString("X4"));
                if (head.CommandId != MessageCommandId.DeviceLoginReq)
                {

                    string Txt = "";
                    try
                    {
                        if (head.MessageLength < Total.Length)
                        {
                            byte[] _tmpData = new byte[head.MessageLength - 8];
                            Array.Copy(Total, 8, _tmpData, 0, _tmpData.Length);
                            Txt = _tmpData.AES_Dencrypt().UTF8ToString();
                        }
                    }
                    catch (Exception ex)
                    {
                        Logger.Error("Receiver3:" + ex.Message);
                    }
                    Logger.Warning($"收到终端消息，但不是设备认证消息，连接已切断！CmdId={head.CommandId.ToString("X4")},Text={Txt}");
                    clientSession.Dispose();
                    return;
                }
                if (Total.Length < head.MessageLength)
                {
                    Logger.Information("2 if (Total.Length < head.MessageLength)");
                    return;
                }
                else
                {
                    Logger.Information("2 if (Total.Length < head.MessageLength)" + head.ToJsonString());
                }
                byte[] bsPayload = new byte[head.MessageLength - IHeader.HeaderLength];
                Array.Copy(Total, IHeader.HeaderLength, bsPayload, 0, bsPayload.Length);
                var bsDecrypt = bsPayload.AES_Dencrypt();
                if (bsDecrypt == null)
                {
                    Logger.Error($"登录消息解密失败data={bsPayload.GetHexString()}");
                    return;
                }
                string strPayLoad = bsDecrypt.UTF8ToString();
                Logger.Information($"收到登录消息 ，ClientSessionId={clientSession.SessionId}，LoginInfo={strPayLoad},ClientRemoteEndPoint={clientSession.GetClientRemoteEndPoint()}");
                DeviceLoginReq loginReq = strPayLoad.ToJsonObject<DeviceLoginReq>();
                if (loginReq == null)
                {
                    Logger.Error($"登录消息解析失败");
                    return;
                }
                var loginResp = OnRecvLogin(clientSession, head, loginReq);
                if (loginResp == null)
                {
                    Logger.Error($"登录系统故障失败");

                    return;
                }
                if (loginResp.DeviceValid != 0 || loginResp.DeviceState != 0)
                {
                    Logger.Error($"登录登录失败");
                    return;
                }
                RemoveNoNameClient(clientSession.SessionId);
                clientSession.ClientId = loginReq.DeviceCode;
                if (AddNamedClient(clientSession) != 0)
                {
                    Logger.Error($"登录添加缓存失败失败");
                    return;
                }
                else
                {
                    dlr = loginResp;
                }
            }
            catch (Exception ex)
            {
                Logger.Error("ReceiveNoNameClientMessageCallBack Error3:" + ex.Message);
                if (clientSession != null && clientSession.ClientSocket != null && clientSession.ClientSocket.Connected == true)
                    clientSession.BeginRecvData(new AsyncCallback(ReceiveNoNameClientMessageCallBack));// clientSession.Dispose();
            }
            finally
            {
                if (clientSession.ClientSocket != null && clientSession.ClientSocket.Connected == true)
                {
                    if (dlr == null)
                    {
                        clientSession.BeginRecvData(new AsyncCallback(ReceiveNoNameClientMessageCallBack));//
                    }
                    else
                    {
                        clientSession.BeginRecvData(new AsyncCallback(ReceiveNamedClientMessageCallBack));
                    }
                }
                else
                {
                    clientSession.Dispose();
                }
                Logger.Warning($"finallyClientInfo={clientSession.ToJsonString()}");//
            }
        }

        private void ReceiveNamedClientMessageCallBack(IAsyncResult ar)
        {
            ClientSession clientSession = ar.AsyncState as ClientSession;
            if (clientSession == null)
            {
                return;
            }

            int RecvSize = 0;  //客户端正常关闭服务器就会接受到空数据,但是不会报错,所以这里处理正常关闭的情况
            try
            {
                RecvSize = clientSession.EndRecvData(ar);
                if (RecvSize == 0)
                {
                    Logger.Warning($",head.Message 断开! ClientInfo={clientSession.ToJsonString()}");//
                    RemoveNamedClient(clientSession.ClientId, clientSession.SessionId);
                    clientSession.Dispose();
                    return;
                }
                Logger.Warning($"chushihua:{RecvSize}:ClientInfo={clientSession.ToJsonString()}");//
            }
            catch (Exception ex)
            {
                Logger.Warning(ex, $"ReceiveNamedClientMessageCallBack Error1,ClientInfo={clientSession.ToJsonString()}");
                RemoveNamedClient(clientSession.ClientId, clientSession.SessionId);
                clientSession.Dispose();
                return;
            }
            try
            {
                clientSession.SetReceiveClientMessageTime();
                byte[] Total = new byte[RecvSize + clientSession.LastBytes.Length];
                clientSession.LastBytes.CopyTo(Total, 0);
                Array.Copy(clientSession.RecvBuff, 0, Total, clientSession.LastBytes.Length, RecvSize);
                clientSession.LastBytes = new byte[0];
                clientSession.RecvBuff.Initialize();
                int index = 0;
                while (index < Total.Length)
                {
                    int Remain = Total.Length - index;
                    if (Remain < 10)
                    {/////////////////////////////?
                        clientSession.LastBytes = new byte[Remain];
                        Array.Copy(Total, index, clientSession.LastBytes, 0, Remain);
                        break;
                    }
                    Header head = null;
                    try
                    {
                        head = new Header(Total, index);
                    }
                    catch (Exception ex)
                    {
                        RemoveNamedClient(clientSession.ClientId, clientSession.SessionId);
                        Logger.Warning(ex, $"ErrorPacket: Total={Convert.ToBase64String(Total)},Index={index},ClientInfo={clientSession.ToJsonString()}");
                        return;
                    }
                    if (Remain < head.MessageLength)
                    {
                        clientSession.LastBytes = new byte[Remain];
                        Array.Copy(Total, index, clientSession.LastBytes, 0, Remain);
                        break;
                    }
                    if (head.MessageLength == 0)
                    {
                        Logger.Warning($",head.Messagelenth=0,连接断开! ClientInfo={clientSession.ToJsonString()}");
                        RemoveNamedClient(clientSession.ClientId, clientSession.SessionId);
                        clientSession.Dispose();
                        return;
                    }
                    byte[] bsMessage = new byte[head.MessageLength];
                    Array.Copy(Total, index, bsMessage, 0, bsMessage.Length);
                    index += head.MessageLength;
                    //直接回应心跳检测
                    if (head.CommandId == MessageCommandId.HeartbeatReq)
                    {
                        Header HeartBeatRsp = new Header()
                        {
                            CommandId = MessageCommandId.HeartbeatRsp,
                            MessageLength = IHeader.HeaderLength,
                            TimeStamp = DateTimeExtend.GetTimeStampS()
                        };
                        clientSession.SendMessage(HeartBeatRsp);
                    }
                    else
                    {
                        var action = this.GetAction(head.CommandId);
                        if (action != null)
                        {
                            try
                            {
                                int result = action(clientSession, head, bsMessage);
                            }
                            catch (Exception ex)
                            {
                                Logger.Warning(ex, $"ReceiveNamedClientMessageCallBack Errorddddddddd1,ClientInfo={clientSession.ToJsonString()}");
                            }
                        }
                        else
                        {
                            Logger.Warning($"Command未识别: CmdId={head.CommandId.ToString("X4")},ClientId={clientSession.ClientId}");
                        }
                    }

                }
            }
            catch (Exception ex)
            {
                Logger.Warning(ex, $"ReceiveNamedClientMessageCallBack Error1,ClientInfo={clientSession.ToJsonString()}");
                RemoveNamedClient(clientSession.ClientId, clientSession.SessionId);
                return;
            }
            finally
            {
                if (clientSession != null && clientSession.ClientSocket != null && clientSession.ClientSocket.Connected == true)
                    clientSession.BeginRecvData(new AsyncCallback(ReceiveNamedClientMessageCallBack));
                else
                {
                    Logger.Warning($"Command未Id={clientSession.ClientId}");
                    clientSession.Dispose();
                }
            }
        }
    }
}
